package utils;

import entity.http.HttpEntity;
import entity.http.HttpRequestEntity;
import entity.http.HttpResponseEntity;
import exception.MinusOneCountException;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.*;

public class HttpUtils {
    private static String lineSeparator = "\r\n";
    private static int bufferSize = 20 * 1024 * 1024;

    public static HttpRequestEntity getHttpRequestEntity(InputStream in) throws IOException {
        String bufferStr = readHeaderString(in);

        if(bufferStr == null || "".equals(bufferStr)){
            return null;
        }

        //取第一行作为target
        String target = bufferStr.substring(0, bufferStr.indexOf(lineSeparator));
        //剩余的解析为headers
        bufferStr = bufferStr.substring(bufferStr.indexOf(lineSeparator) + 2);
        Map<String, String> headers = parseHeaderString(bufferStr);

        HttpRequestEntity entity = new HttpRequestEntity();
        entity.setTarget(target);
        entity.setHeaders(headers);
        return entity;
    }

    public static HttpResponseEntity getHttpResponseEntity(InputStream in) throws IOException {
        String bufferStr = readHeaderString(in);

        if(bufferStr == null || "".equals(bufferStr)){
            return null;
        }

        //取第一行作为target
        String target = bufferStr.substring(0, bufferStr.indexOf(lineSeparator));
        //剩余的解析为headers
        bufferStr = bufferStr.substring(bufferStr.indexOf(lineSeparator) + 2);
        Map<String, String> headers = parseHeaderString(bufferStr);

//        byte[] body = null;
//
//        if(headers.containsKey("Content-Length")){
//            body = readBody(in, Integer.parseInt(headers.get("Content-Length")));
//        }else if("close".equals(headers.get("Connection"))){
//            //如果请求头里Connection字段为close的话，说明没有Content-Length也没关系，直接读取到流结束剩下的部分就是body了
//            body = readBody(in);
//        }

        HttpResponseEntity entity = new HttpResponseEntity();
        entity.setTarget(target);
        entity.setHeaders(headers);
        return entity;
    }

    public static byte[] readLine(InputStream in) throws IOException, MinusOneCountException {
        int lineSeparatorCount = 0;
        byte tmp = 0;
        byte[] buffer = new byte[bufferSize];
        int index = 0;

        while(true){
            tmp = (byte)in.read();
            //这个方法只有接受chunked编码的数据才会用到，按照chunked的报文格式来看，读到\r\n就表示结束，不用等到读到-1，但是这里为啥读到-1了
            //加了这个if会让百度打不开
//            if(tmp == -1){
//                break;
//            } else
            if(tmp == '\n' || tmp == '\r'){
                lineSeparatorCount++;
            }else{
                lineSeparatorCount = 0;
            }

            buffer[index++] = tmp;

            if(index == bufferSize / 2){
                System.out.println("Warning, a chunked data line's size is over than half bufferSize");

                //不知道为啥读取公司平台的chunked编码数据读取不到length为0的那一行，导致chunked这里读取一行的方法爆了
                //只能判断是不是读了一堆-1来判断，并扔出自定义异常
                int minusOneCount = 0;
                for(int i = 0; i < index; i++){
                    if(buffer[i] == -1){
                        minusOneCount++;
                    }
                }

                if (minusOneCount == index){
                    throw new MinusOneCountException();
                }
            }

            if(lineSeparatorCount == 2){
                break;
            }
        }

        return Arrays.copyOf(buffer, index);
    }

    public static byte[] httpEntityToBytes(HttpEntity entity) throws UnsupportedEncodingException {
        StringBuilder sb = new StringBuilder();

        sb.append(entity.getTarget());
        sb.append(lineSeparator);

        for(Map.Entry<String, String> header : entity.getHeaders().entrySet()){
            sb.append(header.getKey());
            sb.append(": ");
            sb.append(header.getValue());
            sb.append(lineSeparator);
        }
        sb.append(lineSeparator);

        byte[] result = sb.toString().getBytes(StandardCharsets.UTF_8);
        return result;
    }

    public static void requestEntityDecorator(HttpRequestEntity entity, String currentHost){
        if(entity.getHeaders().containsKey("Host")){
            entity.getHeaders().put("Host", currentHost);
        }

        if(entity.getHeaders().containsKey("Referer")){
            entity.getHeaders().put("Referer", entity.getHeaders().get("Referer").replace("localhost", currentHost));
        }
    }

    public static void messageTransport(InputStream source, OutputStream out, HttpEntity entity) throws Exception{
        out.write(httpEntityToBytes(entity));

        if(entity.getHeaders().containsKey("Content-Length")){
            int length = Integer.parseInt(entity.getHeaders().get("Content-Length"));
            SocketConnectUtils.byteTransfer(source, out, length);
        }else if(entity instanceof HttpResponseEntity && "close".equals(entity.getHeaders().get("Connection")) ){
            SocketConnectUtils.byteTransfer(source, out);
        }
    }


    private static String readHeaderString(InputStream in) throws IOException {
        int separatorCount = 0;
        byte[] buffer = new byte[bufferSize];
        int bytesRead = 0;
        int tmp = -2;
        String result = "";

        while(bytesRead < bufferSize && (tmp = in.read()) != -1){

            buffer[bytesRead++] = (byte)tmp;

            //当连续读取到4个\r或\n，说明已经读到header的最后了，否则重置计数器
            if(tmp == '\r' || tmp == '\n'){
                separatorCount++;
            }else{
                separatorCount = 0;
            }

            if(separatorCount == 4){
                break;
            }
        }

        result = new String(buffer, 0, bytesRead, "UTF-8");

        return result;
    }

    private static Map<String, String> parseHeaderString(String headerString){
        String currentLine = "";
        Map<String, String> headers = new HashMap<>();

        while(true){
            currentLine = headerString.substring(0, headerString.indexOf(lineSeparator));

            //已解析到最后的两个\r\n处，退出循环
            if("".equals(currentLine)){
                break;
            }

            headers.put(
                    currentLine.substring(0, currentLine.indexOf(':')),
                    currentLine.substring(currentLine.indexOf(':') + 2, currentLine.length())
            );
            headerString = headerString.substring(headerString.indexOf(lineSeparator) + 2);
        }

        return headers;
    }

    /**
     * 一个字节一个字节的读，防止由于tcp半包导致的一部分数据没有读到
     * @param in
     * @param length
     * @return
     */
    private static byte[] readBody(InputStream in, int length){
        byte[] buffer = new byte[length];
        int tmp = -2;
        try {
            for(int i = 0; i < length; i++){
                tmp = in.read();
                buffer[i] = (byte)tmp;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return buffer;
    }

    /**
     * 此方法一直读取数据直到流结束
     * @param in
     * @return
     */
    private static byte[] readBody(InputStream in) throws IOException{
        int index = 0;
        byte[] buffer = new byte[bufferSize];
        int tmp = -1;

        while((tmp = in.read()) != -1){
            buffer[index++] = (byte)tmp;
        }

        return Arrays.copyOf(buffer, index);
    }

    private static byte[] combineByteArray(byte[] a, byte[] b){
        if(b == null){
            return a;
        }

        byte[] c = new byte[a.length + b.length];
        int index = 0;

        for(byte t : a){
            c[index++] = t;
        }
        for(byte t : b){
            c[index++] = t;
        }

        return c;
    }

    public static boolean isRedirectResponse(HttpResponseEntity entity){
        if(entity.getResponseCode() % 300 < 100){
            return true;
        }
        else{
            return false;
        }
    }
}
